﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using UnityEngine;

namespace HappyStudent.CfgLoad
{
    public class CfgLoad
    {
        static readonly Types types = new Types();

        public static void LoadCfg(string LoadCfgPath, ConcurrentDictionary<string, PropertyInfo> properties)
        {

            Directory.CreateDirectory(LoadCfgPath);
            DirectoryInfo directoryInfo = new DirectoryInfo(LoadCfgPath);
            if (directoryInfo.Exists)
            {
                var subdirectories = directoryInfo.GetDirectories();//获取全部子目录
                if (subdirectories.Length <= 0) return;
                try
                {

                    ConcurrentDictionary<string, ValueTuple<int, string>> cmpjson = new ConcurrentDictionary<string, ValueTuple<int, string>>();//冲突文件
                    ConcurrentDictionary<ValueTuple<int, string>, string> cmp = new ConcurrentDictionary<ValueTuple<int, string>, string>();//冲突mod提示 
                    foreach (var subdir in subdirectories)
                    {
                        //Debug.LogWarning($"加载:{subdir.Name}mod");
                        var files = subdir.GetFiles("*.json");
                      //  foreach (var fileInfo in files)
                        Parallel.ForEach(files, fileInfo => // 获取子目录所有JSON文件
                        {

                            //Debug.Log($"加载:{fileInfo}");
                          string jsonName = Path.GetFileNameWithoutExtension(fileInfo.Name);
                            if (properties.TryGetValue(jsonName, out var property))
                            {
                                var text = File.ReadAllText(fileInfo.FullName);
                                try
                                {
                                    var data = JsonConvert.DeserializeObject<Dictionary<int, JObject>>(text);
                                    var value = property.GetValue(null);
                                    if (data != null && value != null && value is IDictionary dic)
                                    {
                                        foreach (var entry in data)
                                        {
                                            object obj;
                                            bool needAdd = false;
                                            var dictype = dic.GetType().GetGenericArguments()[1];
                                            var cfgIndex = ValueTuple.Create(entry.Key, dictype.Name);
                                            if (dic.Contains(entry.Key))//存在id 
                                            {
                                                if (cmp.ContainsKey(cfgIndex)) //存在冲突
                                                {
                                                    if (cmpjson.TryGetValue(dictype.Name, out var cfgvalue))
                                                    {
                                                        cfgvalue.Item2 += " : " + subdir.Name;
                                                    }
                                                    else
                                                    {
                                                        cmpjson.TryAdd(dictype.Name, (entry.Key, subdir.Name + " : " + cmp[cfgIndex]));
                                                    }
                                                    cmp.TryAdd(cfgIndex, subdir.Name);//更新值
                                                }
                                                obj = dic[entry.Key];//获取需要修改值
                                            }
                                            else//添加值
                                            {
                                                needAdd = true;
                                                try
                                                {
                                                    obj = Activator.CreateInstance(dictype);
                                                }
                                                catch (Exception e)
                                                {
                                                    obj = null;
                                                    Debug.LogError(e.ToString());
                                                    needAdd = false;
                                                }
                                            }

                                            foreach (var prp in obj.GetType().GetFields())//遍历值
                                            {
                                                if (entry.Value.TryGetValue(prp.Name, out var token))
                                                {
                                                    var temp = prp.GetValue(obj);//从obj中获取属性
                                                                                 // Debug.Log($"temp:type({temp.GetType()}) value({temp}) {prp}");
                                                    if (temp != null)
                                                    {

                                                        if (token is JArray array && temp is IList em)
                                                        {

                                                            CopyJArrayToList(array, em);
                                                            prp.SetValue(obj, em);
                                                        }
                                                        else
                                                        {
                                                            var type = temp.GetType().TypeHandle.Value;
                                                            CopyTonkeToObj(prp, token, type, obj);
                                                        }
                                                    }
                                                }
                                            }
                                            if (needAdd)//无冲突
                                            {
                                                cmp.TryAdd(cfgIndex, subdir.Name);//添加新值 
                                                dic.Add(entry.Key, obj);
                                            }
                                        }
                                    }
                                }
                                catch (JsonReaderException e)
                                {
                                    CfgLoadException.JsonReadException(subdir.Name, fileInfo.Name, e);
                                }
                            }

                        });
                    }

                    foreach (var errrjson in cmpjson)
                    {
                        Debug.LogError($" Conflict:  Mod Name:[{errrjson.Value.Item2}] Json Name:[{errrjson.Key}]  Id:[{errrjson.Value.Item1}]");
                    }
                }
                catch (Exception e)
                {
                    Debug.LogError(e.ToString());
                }
            }
            else
            {
                Debug.LogError("Configuration directory creation failed, please check the configuration load path");
            }
        }


        private static void CopyJArrayToList(JArray jArray, IList ilist)
        {
            if (jArray.Count < ilist.Count)
            {
                for (int i = ilist.Count - 1; i >= jArray.Count; i--)
                {
                    ilist.RemoveAt(i);//清除多余值
                }
            }
            for (int i = 0; i < jArray.Count; i++)
            {
                if (jArray[i] is JArray array && ilist[i] is IList list)
                {
                    CopyJArrayToList(array, list);
                }
                else
                {
                    if (i < ilist.Count)
                    {
                        var ans = CopyJTokenToObj(jArray[i], ilist[i]);
                        if (ans != null)
                        {
                            ilist[i] = ans;
                        }
                    }
                    else
                    {
                        var ans = CopyJTokenToObj(jArray[i], ilist[0]);
                        if (ans != null)
                            ilist.Add(ans);//添加额外值
                    }
                }
            }
        }

        private static object CopyJTokenToObj(JToken token, object obj)
        {
            var type = obj.GetType().TypeHandle.Value;
            if (type == types.String)
            {
                return token.Value<string>();
            }
            else if (type == types.Int)
            {
                return token.Value<int>();

            }
            else if (type == types.UInt)
            {
                return token.Value<uint>();
            }
            else if (type == types.Bool)
            {
                return token.Value<bool>();
            }
            else if (type == types.Byte)
            {
                return token.Value<byte>();
            }
            else if (type == types.SByte)
            {
                return token.Value<sbyte>();

            }
            else if (type == types.Char)
            {
                return token.Value<char>();
            }
            else if (type == types.Short)
            {
                return token.Value<short>();
            }
            else if (type == types.UShort)
            {
                return token.Value<ushort>();
            }
            else if (type == types.Long)
            {
                return token.Value<long>();
            }
            else if (type == types.ULong)
            {
                return token.Value<ulong>();
            }
            else if (type == types.Decimal)
            {
                return token.Value<decimal>();
            }
            else if (type == types.Float)
            {
                return token.Value<float>();
            }
            else if (type == types.Double)
            {
                return token.Value<double>();
            }
            return null;
        }
        private static void CopyTonkeToObj(FieldInfo prp, JToken token, IntPtr type, object temp)
        {
            if (type == types.String)
            {
                var ss = token.Value<string>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Int)
            {
                var ss = token.Value<int>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.UInt)
            {
                var ss = token.Value<uint>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Bool)
            {
                var ss = token.Value<bool>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Byte)
            {
                var ss = token.Value<byte>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.SByte)
            {
                var ss = token.Value<sbyte>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Char)
            {
                var ss = token.Value<char>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Short)
            {
                var ss = token.Value<short>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.UShort)
            {
                var ss = token.Value<ushort>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Long)
            {
                var ss = token.Value<long>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.ULong)
            {
                var ss = token.Value<ulong>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Decimal)
            {
                var ss = token.Value<decimal>();
                prp.SetValue(temp, ss);

            }
            else if (type == types.Float)
            {
                var ss = token.Value<float>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.Double)
            {
                var ss = token.Value<double>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.TimeSpan)
            {

                var ss = token.Value<TimeSpan>();
                prp.SetValue(temp, ss);
            }
            else if (type == types.DateTime)
            {
                var ss = token.Value<DateTime>();

                prp.SetValue(temp, ss);
            }
            else if (type == types.Guid)
            {
                var ss = token.Value<Guid>();
                prp.SetValue(temp, ss);
            }
        }
    }
}
